package entity;

import entity.*;

import java.util.ArrayList;
import java.util.List;

// 符号表
public class SymbolTable {
    private List<Const> constList;
    private List<Variable> variableList;
    private List<Function> functionList;

    private int tempIndex;

    // 检查函数是否定义
    public boolean isDefinedFunction(String name){
        // 1、检查在常量表中是否有该标识
        for (int i = 0; i < constList.size(); i++) {
            if (constList.get(i).getName().equals(name)){
                return true;
            }
        }

        // 2、检查在变表中是否有该标识
        for (int i = 0; i < variableList.size(); i++) {
            if (variableList.get(i).getName().equals(name)){
               return true;
            }
        }

        // 3、检查在函数表中是否有该标识
        for (int i = 0; i < functionList.size(); i++) {
            if (functionList.get(i).getName().equals(name)){
                return true;
            }
        }

        // 4、都没有 则未被定义
        return false;
    }

    // 增加函数定义
    public void addFunction(String name, String returnType, Integer parameterNumber, String... parameters){
        List<String> parameterList = new ArrayList<>();
        if(parameters != null){
            for (int i = 0; i < parameters.length; i++) {
                parameterList.add(parameters[i]);
            }
        }
        Function function = new Function(name, returnType, parameterNumber, parameterList);
        functionList.add(function);
    }

    // 检查变量使用前是否定义
    public boolean isDefinedVariable(Word word, String localScope){
        // 1、检查在常量表中是否有该标识
        for (int i = 0; i < constList.size(); i++) {
            if (constList.get(i).getName().equals(word.getWord())){
                return true;
            }
        }
        // 2、检查在函数表中是否有该标识
        for (int i = 0; i < functionList.size(); i++) {
            if (functionList.get(i).getName().equals(word.getWord())){
                return true;
            }
        }
        // 3、检查在变表中是否有该标识
        for (int i = 0; i < variableList.size(); i++) {
            // 3.1、先寻找名字相同
            if (variableList.get(i).getName().equals(word.getWord())){
                // 3.2、获取同名变量表
                List<VariableWSN> variableWSN = variableList.get(i).getVariableWSN();
                // 3.3、遍历同名变量表
                for (int m = 0; m < variableWSN.size(); m++) {
                    // 3.4、查看当前作用域
                    if(variableWSN.get(m).getScope().length() == localScope.length()){
                        if(variableWSN.get(m).getScope().equals(localScope)){
                            return true;
                        }
                    }else if(variableWSN.get(m).getScope().length() < localScope.length()){
                        if (localScope.startsWith(variableWSN.get(m).getScope())) {
                            return true;
                        }
                    }
                }
            }
        }

        // 4、都没有 则未被定义
        return false;
    }

    // 检查当前区域变量是否定义
    public boolean isDefinedVariableInLocalScope(Word word, String localScope){
        // 3、检查在变表中是否有该标识
        for (int i = 0; i < variableList.size(); i++) {
            // 3.1、先寻找名字相同
            if (variableList.get(i).getName().equals(word.getWord())){
                // 3.2、获取同名变量表
                List<VariableWSN> variableWSN = variableList.get(i).getVariableWSN();
                // 3.3、遍历同名变量表
                for (int m = 0; m < variableWSN.size(); m++) {
                    // 3.4、查看当前作用域
                    if(variableWSN.get(m).getScope().equals(localScope)){
                        return true;
                    }
                }
            }
        }

        // 4、都没有 则未被定义
        return false;
    }

    public VariableWSN createNewVariableWSN(String scope){
        String name = "";
        for (; ; ) {
            name = "T" + tempIndex;
            tempIndex++;
            if(!isDefinedVariable(new Word(0, name, -1), scope)){
                VariableWSN vWSN = new VariableWSN(name, scope, null, null);
                List<VariableWSN> temp = new ArrayList<>();
                temp.add(vWSN);
                Variable variable = new Variable(name, temp);
                variableList.add(variable);
                return vWSN;
            }
        }
    }

    public VariableWSN findVariableByNameAndScope(String name, String localScope){
        for (int i = 0; i < variableList.size(); i++) {
            // 3.1、先寻找名字相同
            if (variableList.get(i).getName().equals(name)){
                // 3.2、获取同名变量表
                List<VariableWSN> variableWSN = variableList.get(i).getVariableWSN();
                // 3.3、遍历同名变量表
                for (int m = 0; m < variableWSN.size(); m++) {
                    // 3.4、查看当前作用域
                    if(variableWSN.get(m).getScope().length() == localScope.length()){
                        if(variableWSN.get(m).getScope().equals(localScope)){
                            return variableWSN.get(m);
                        }
                    }else if(variableWSN.get(m).getScope().length() < localScope.length()){
                        if (localScope.startsWith(variableWSN.get(m).getScope())) {
                            return variableWSN.get(m);
                        }
                    }
                }
            }
        }
        for (int i = 0; i < constList.size(); i++) {
            if(constList.get(i).getName().equals(name)){
                return ConstToVariable(constList.get(i));
            }
        }
        return null;
    }

    private VariableWSN ConstToVariable(Const aConst) {
        return new VariableWSN(aConst.getName(), "/0", aConst.getType(), aConst.getVal());
    }

    // 增加变量
    public void addVariable(String name, String localScope, String type, Object val){
        boolean flag = true;
        for (int i = 0; i < variableList.size(); i++) {
            if (variableList.get(i).getName().equals(name)){
                List<VariableWSN> variableWSN = variableList.get(i).getVariableWSN();
                variableWSN.add(new VariableWSN(name, localScope, type, val));
                variableList.get(i).setVariableWSN(variableWSN);
                flag = false;
                break;
            }
        }
        if (flag){
            Variable variable = new Variable(name, new ArrayList<>());
            variable.getVariableWSN().add(new VariableWSN(name, localScope, type, val));
            variableList.add(variable);
        }
    }

    // 输出变量表
    public String printVariableList(){
        String str = "";
        if(variableList.size() > 0){
            str += "变量表：\n";
            for (Variable variable : variableList) {
                str += variable.toString() + "\n";
            }
        }else {
            str += "变量表为空\n";
        }
        System.out.println(str);
        return str;
    }

    // 检查常量是否定义
    public boolean isDefinedConst(String name){
        // 1、检查在变量表中是否有该标识
        for (int i = 0; i < variableList.size(); i++) {
            if (variableList.get(i).getName().equals(name)){
                return true;
            }
        }
        // 2、检查在函数表中是否有该标识
        for (int i = 0; i < functionList.size(); i++) {
            if (functionList.get(i).getName().equals(name)){
                return true;
            }
        }

        // 3、检查在常量表中是否有该标识
        for (int i = 0; i < constList.size(); i++) {
            if (constList.get(i).getName().equals(name)){
                return true;
            }
        }

        // 4、都没有 则未被定义
        return false;
    }

    // 增加常量
    public void addConst(String name, String type, Object val){
//        if(type.equals("int")){
//            addConst(name, type, (int) val);
//        } else if(type.equals("float")){
//            addConst(name, type, (float) val);
//        } else if(type.equals("char")){
//            addConst(name, type, (char) val);
//        }else {
//
//        }
        Const temp = new Const(name, type, val);
        constList.add(temp);
    }

    // 增加变量 int
    public void addConst(String name, String type, int val){
        Const temp = new Const(name, type, val);
        constList.add(temp);
    }

    // 增加变量 float
    public void addConst(String name, String type, float val){
        Const temp = new Const(name, type, val);
        constList.add(temp);
    }

    // 增加变量 char
    public void addConst(String name, String type, char val){
        Const temp = new Const(name, type, val);
        constList.add(temp);
    }

    // 输出常量表
    public String printConstList(){
        String str = "";
        if(constList.size() > 0){
            str += "常量表：\n";
            for (Const aConst : constList) {
                str += aConst.toString() + "\n";
            }
        }else {
            str += "常量表为空\n";
        }
        System.out.println(str);
        return str;
    }

    // 输出常量表
    public String printFunctionList(){
        String str = "";
        if(functionList.size() > 0){
            str += "函数定义：\n";
            for (Function function : functionList) {
                str += function.toString() + "\n";
            }
        }else {
            str += "函数定义为空\n";
        }
        System.out.println(str);
        return str;
    }

    public SymbolTable() {
        constList = new ArrayList<>();
        variableList = new ArrayList<>();
        functionList = new ArrayList<>();
        tempIndex = 1;
    }

    public List<Const> getConstList() {
        return constList;
    }

    public void setConstList(List<Const> constList) {
        this.constList = constList;
    }

    public List<Variable> getVariableList() {
        return variableList;
    }

    public void setVariableList(List<Variable> variableList) {
        this.variableList = variableList;
    }

    public List<Function> getFunctionList() {
        return functionList;
    }

    public void setFunctionList(List<Function> functionList) {
        this.functionList = functionList;
    }

    public int getTempIndex() {
        return tempIndex;
    }

    public void setTempIndex(int tempIndex) {
        this.tempIndex = tempIndex;
    }

    @Override
    public String toString() {
        return "SymbolTable{" +
                "constList=" + constList +
                ", variableList=" + variableList +
                ", functionList=" + functionList +
                '}';
    }
}
